package com.wwd.movie.login.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wwd.movie.common.AjaxResult;
import com.wwd.movie.common.UserContext;
import com.wwd.movie.common.constant.CacheConstants;
import com.wwd.movie.common.exception.BusinessException;
import com.wwd.movie.common.service.TokenService;
import com.wwd.movie.common.utils.RedisCache;
import com.wwd.movie.common.utils.TencentCosUtil;
import com.wwd.movie.login.domain.dto.LoginDTO;
import com.wwd.movie.login.domain.dto.LoginUser;
import com.wwd.movie.login.domain.dto.UpdatePwdDTO;
import com.wwd.movie.login.domain.entity.MovieUser;
import com.wwd.movie.login.domain.vo.GetUserProfileVO;
import com.wwd.movie.login.mapper.MovieUserMapper;
import com.wwd.movie.login.service.MovieUserService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Base64;

/**
 * @author sendpo
 * @description 针对表【movie_user(普通用户表)】的数据库操作Service实现
 * @createDate 2025-08-29 13:54:08
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MovieUserServiceImpl extends ServiceImpl<MovieUserMapper, MovieUser>
        implements MovieUserService {

    private final RedisCache redisCache;
    private final TokenService tokenService;
    private final MovieUserMapper movieUserMapper;
    private final TencentCosUtil tencentCosUtil;
    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Resource
    private AuthenticationManager authenticationManager;

    @Override
    public String login(String username, String password, String code, String uuid) {
        // 1. 校验验证码
        validateCaptcha(code, uuid);

        // 2. 登录前置校验（用户名、密码非空等）
        loginPreCheck(username, password);

        // 3. 构造认证对象
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);

        // 4. 调用 Spring Security 认证
        Authentication authentication;
        try {
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            throw new RuntimeException("用户名或密码错误");
        }

        // 5. 获取登录用户信息
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

        // 6. 记录最后登录时间
        movieUserMapper.updateLastLoginTime(loginUser.getUser().getId(), LocalDateTime.now());

        // 7. 生成 JWT Token
        return tokenService.createToken(loginUser);

    }

    public void validateCaptcha(String code, String uuid) {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StrUtil.blankToDefault(uuid, "");
        String captcha = redisCache.get(verifyKey);
        redisCache.delete(verifyKey);
        if (captcha == null) {
            throw new BusinessException("验证码已失效");
        }
        if (!code.equalsIgnoreCase(captcha)) {
            throw new BusinessException("验证码错误");
        }
    }

    public void loginPreCheck(String username, String password) {
        if (StrUtil.isBlank(username)) {
            throw new BusinessException("用户名不能为空");
        }
        if (StrUtil.isBlank(password)) {
            throw new BusinessException("密码不能为空");
        }
        if (password.length() < 6 || password.length() > 20) {
            throw new BusinessException("密码长度必须介于 6-20 之间");
        }
        if (username.length() < 3 || username.length() > 20) {
            throw new BusinessException("用户名长度必须介于 3-20 之间");
        }

    }

    @Override
    public AjaxResult register(LoginDTO dto) {
        validateCaptcha(dto.getCode(), dto.getUuid());
        loginPreCheck(dto.getUsername(), dto.getPassword());
        MovieUser movieUser = movieUserMapper.selectByUsername(dto.getUsername());
        if (movieUser != null) {
            return AjaxResult.error("用户已存在");
        }
        movieUser = new MovieUser();
        movieUser.setUsername(dto.getUsername());
        movieUser.setNickname(dto.getUsername());
        movieUser.setPassword(passwordEncoder.encode(dto.getPassword()));
        movieUserMapper.insert(movieUser);
        return AjaxResult.success("注册成功");
    }

    @Override
    public AjaxResult updateUserProfile(GetUserProfileVO vo) {
        MovieUser user = new MovieUser();
        user.setId(vo.getId());
        user.setNickname(vo.getNickname());
        user.setAvatar(vo.getAvatar());
        int i = movieUserMapper.updateById(user);
        return i > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    @Override
    public AjaxResult updatePwd(UpdatePwdDTO dto) {
        MovieUser user = UserContext.getUser();
        if (!passwordEncoder.matches(dto.getOldPassword(), user.getPassword())) {
            return AjaxResult.error("旧密码错误");
        }
        if (dto.getNewPassword().length() < 6 || dto.getNewPassword().length() > 20) {
            return AjaxResult.error("密码长度必须介于 6-20 之间");
        }
        if (dto.getNewPassword().equals(dto.getOldPassword())) {
            return AjaxResult.error("旧密码不能与新密码相同");
        }
        user.setPassword(passwordEncoder.encode(dto.getNewPassword()));
        int i = movieUserMapper.updateById(user);
        UserContext.clear();
        return i > 0 ? AjaxResult.success() : AjaxResult.error();
    }


    private static final String IMGBB_API_KEY = "3b3c66a59906ea95a3e6d278785c2297";
    private static final String IMGBB_UPLOAD_URL = "https://api.imgbb.com/1/upload?key=" + IMGBB_API_KEY;

    private static final String PICGO_API_KEY = "chv_SRUdL_dd717704a8fe8453ae5452e0bae9b368ae57787bbccd418ba0281ea271ba36acd71a5b0f95fd9bae8763d01b29728ba2cdb2c0ae262008315c91f54fdcb2541b";
    private static final String PICGO_UPLOAD_URL = "https://www.picgo.net/api/1/upload";

    @Override
    public AjaxResult uploadAvatar(MultipartFile file) {
        // 先尝试 picgo 上传
        AjaxResult result = uploadToPicgo(file);
        if (result.get("code").equals(200)) {
            log.info("上传成功，使用 picgo");
            return result;
        }

        // 如果 picgo 失败，尝试 imgbb
        result = uploadToImgbb(file);
        if (result.get("code").equals(200)) {
            log.info("上传成功，使用 imgbb");
            return result;
        }

        result = uploadToTencentOSS(file, "movie/avatar/");
        if (result.get("code").equals(200)) {
            log.info("上传成功，使用 tencent oss");
            return result;
        }


        return AjaxResult.error("图片上传失败");
    }

    private AjaxResult uploadToTencentOSS(MultipartFile file, String s) {
        String url = null;
        try {
            url = tencentCosUtil.upload(file, s);
        } catch (Exception e) {
            return AjaxResult.error("腾讯OSS 上传异常：" + e);
        }
        return saveUserAvatar(url);
    }

    private AjaxResult uploadToPicgo(MultipartFile file) {
        try {
            HttpResponse response = HttpRequest.post(PICGO_UPLOAD_URL)
                    .form("key", PICGO_API_KEY)
                    .form("source", cn.hutool.core.codec.Base64.encode(file.getBytes()))
                    .execute();

            if (response.getStatus() == 200) {
                JSONObject json = JSONUtil.parseObj(response.body());
                int statusCode = json.getInt("status_code", 500);
                if (statusCode == 200) {
                    String url = json.getJSONObject("image").getStr("url");
                    return saveUserAvatar(url);
                }
            }
            return AjaxResult.error("Picgo 上传失败: " + response.body());
        } catch (Exception e) {
            return AjaxResult.error("Picgo 上传异常：" + e);
        }
    }

    private AjaxResult uploadToImgbb(MultipartFile file) {
        try {
            byte[] bytes = file.getBytes();
            String base64Image = Base64.getEncoder().encodeToString(bytes);

            HttpResponse response = HttpRequest.post(IMGBB_UPLOAD_URL)
                    .form("key", IMGBB_API_KEY)
                    .form("image", base64Image)
                    .execute();

            if (response.getStatus() == 200) {
                JSONObject json = JSONUtil.parseObj(response.body());
                if (json.getBool("success")) {
                    String url = json.getJSONObject("data").getStr("url");
                    return saveUserAvatar(url);
                }
            }
            return AjaxResult.error("Imgbb 上传失败: " + response.body());
        } catch (Exception e) {
            return AjaxResult.error("Imgbb 上传异常：" + e.getMessage());
        }
    }

    private AjaxResult saveUserAvatar(String url) {
        MovieUser user = UserContext.getUser();
        user.setAvatar(url);
        int i = movieUserMapper.updateById(user);
        return i > 0 ? AjaxResult.success(url) : AjaxResult.error("保存用户头像失败");
    }

}




